package redora.configuration.rdo.gwt.mvp.service;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.*;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import redora.client.constants.RedoraMessages;
import redora.client.mvp.ClientFactory;
import redora.configuration.rdo.gwt.mvp.UpgradeEvent;
import redora.configuration.rdo.gwt.mvp.service.base.RedoraConfigurationServiceBase;

import java.util.logging.Logger;

import static com.google.gwt.http.client.RequestBuilder.GET;
import static com.google.gwt.http.client.Response.SC_OK;
import static java.util.logging.Level.SEVERE;
import static redora.client.util.GWTViewUtil.displayError;

/**
 * Service object for RedoraConfiguration that will do the communication with the server.
 * Implemented as singleton to allow caching of objects on the client side.<br>
 * Redora has generated the standard service methods in {@link RedoraConfigurationServiceBase},
 * you can add your custom methods here.
 *
 * @author Redora (www.redora.net)
 */
public class RedoraConfigurationService extends RedoraConfigurationServiceBase {

    static Logger l = Logger.getLogger("RedoraConfigurationService");
    static RedoraMessages redoraMessages = GWT.create(RedoraMessages.class);

    //As singleton: allows to cache result sets and pojos
    static RedoraConfigurationService instance;

    private RedoraConfigurationService(ClientFactory clientFactory) {
        super(clientFactory);
    }

    public static synchronized RedoraConfigurationService getInstance() {
        if (instance == null)
            instance = new RedoraConfigurationService(GWT.<ClientFactory>create(ClientFactory.class));
        return instance;
    }

    @Override
    public int finderStepSize() {
        return 50;
    }

    @Override
    public String getAuthInfo() {
        return "";
    }

    @Override
    public boolean customResponse(int status, JSONObject responseJSON) {
        return true;
    }

    public enum Action {
        upgrade, check, dropAll, getScriptContent, getScriptOutput, findModels, findModelFields
    }

    final String baseUpgradeUrl = "../redoraOperations.do?";

    public void upgradeTable() {
        final String _url = URL.encode(baseUpgradeUrl + "_operationType=upgradeObjectTable");
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, _url);
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + _url, e);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(response.getText()).isObject().get("response").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.upgrade.name()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + _url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            displayError(redoraMessages.responseErrorConnect());
            l.log(SEVERE, "Failed to upgrade data tables");
        }
    }

    public void checkTable() {
        final String _url = URL.encode(baseUpgradeUrl + "_operationType=checkObjectField");
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, _url);
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + _url, e);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(response.getText()).isObject().get("response").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.check.name(), responseJSON.get("data").isString().stringValue()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + _url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            displayError(redoraMessages.responseErrorConnect());
            l.log(SEVERE, "Failed to check data tables");
        }
    }

    public void dropAllTable() {
        final String _url = URL.encode(baseUpgradeUrl + "_operationType=dropAllTables");
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, _url);
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + _url, e);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(response.getText()).isObject().get("response").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.dropAll.name()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + _url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            displayError(redoraMessages.responseErrorConnect());
            l.log(SEVERE, "Failed to drop data tables");
        }
    }

    public void getScriptContent(String scriptName) {
        final String _url = URL.encode(baseUpgradeUrl + "_operationType=getScriptFileContent&scriptName="
                + scriptName);
        RequestBuilder builder = new RequestBuilder(GET, _url);
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + _url, e);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(response.getText()).isObject().get("response").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.getScriptContent.name(), responseJSON.get("data").isString().stringValue()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + _url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            displayError(redoraMessages.responseErrorConnect());
            l.log(SEVERE, "Failed to get default sql script " + scriptName + "'s content");
        }
    }

    public void getScriptOutput(String id) {
        final String _url = URL.encode(baseUpgradeUrl + "_operationType=getScriptOutput&id=" + id);
        RequestBuilder builder = new RequestBuilder(GET, _url);
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + _url, e);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(response.getText()).isObject().get("response").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.getScriptOutput.name(), responseJSON.get("data").isString().stringValue()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + _url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            l.log(SEVERE, "Failed to get script output from server", e);
            displayError(redoraMessages.responseErrorConnect());
        }
    }

    public void findModels() {
        final String url = baseUpgradeUrl + "_operationType=getObjectNames";
        RequestBuilder builder = new RequestBuilder(GET, url);
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + url, e);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(
                            response.getText()).isObject().get("response").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.findModels.name(), responseJSON.get("data").isArray().toString()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            displayError(redoraMessages.responseErrorConnect());
            l.log(SEVERE, "Failed to get model list from server", e);
        }
    }

    public void findModelFields(String objectName) {
        final String _url = URL.encode(baseUpgradeUrl + "_operationType=getFieldsByObjectName&objectName=" + objectName);
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, _url);
        builder.setCallback(new RequestCallback() {

            public void onError(Request request, Throwable exception) {
                displayError(redoraMessages.responseErrorConnect());
                l.log(SEVERE, "Request failed " + _url, exception);
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = JSONParser.parseLenient(
                            response.getText()).isObject().get("response").isObject();
                    JSONObject object = responseJSON.get("data").isObject();
                    clientFactory.getEventBus().fireEvent(new UpgradeEvent(Action.findModelFields.name(), object.toString()));
                } else {
                    displayError(redoraMessages.responseErrorNot200());
                    l.log(SEVERE, "Request failed, status " + response.getStatusCode() + ", url: " + _url);
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            displayError(redoraMessages.responseErrorConnect());
            l.log(SEVERE, "Failed to get model names from server", e);
        }
    }
}